<html>
    <head>
        <title>状态快照</title>
    </head>
    <body>
        <script>
            // 状态快照
                    /*
                        状态变量可能看起来像你可以读取和写入的常规JavaScript变量。
                        但是，状态的行为更像是快照。设置它(调用状态设置函数)不会更改你已有的状态变量，而是会触发重新渲染。
                    */


                    // 一、设置状态触发渲染
                        /*
                            你可能认为你的用户界面会直接响应用户事件(eg:点击)而发生变化。
                            在React中，它的工作方式与这种"心智模型"略有不同。
                            在上一节"渲染和提交"中，你看到了来自React的"设置状态触发重新渲染"。这意味着要让界面对事件做出反应(react),你需要更新状态。
                        */
                            // eg：这是一个发送信息的例子：
                                  import {useState} from 'react';

                                  // 发送消息
                                  function sendMessage(message) {
                                    //.....
                                  }

                                  export default function Form(){
                                    const [isSent,setIsSent] =useState(false);
                                    const [message,setMessage]= useState('Hi');

                                    return (
                                        <Form onSubmit={(e)=>{
                                                                e.preventDefault();
                                                                setIsSent(true);
                                                                sendMessage(message);
                                                             }}>
                                            <textarea placeholder="message"  value={message} onChange={()=>setMessage(e.target.value)} />
                                             
                                            <button type="submit" >Send</button>
                                        </Form>
                                    );
                                  }
                                  
                    // 二、渲染时及时拍照
                            /*
                                "渲染"表示React正在调用你的组件，这是一个函数。
                                从该函数返回的JSX就像是"UI的及时快照"。它的属性、事件处理程序和局部变量都是使用渲染时的状态来计算的。

                                与照片或电影画面不同，你返回的UI"快照"是交互式的。它包括类似事件处理程序的逻辑，用于指定相应输入时发生的事情。
                                React更新屏幕以匹配此快照并连接事件处理程序。

                                当React重新渲染一个组件时：
                                   1、React再次调用你的函数
                                   2、你的函数返回一个新的JSX快照
                                   3、然后React更新屏幕以匹配函数返回的快照。
                            */

                            /*
                                作为组件的内存，状态不像一个常规变量，在你的组件渲染(函数返回)后状态变量不会消失。
                                React将此时你的组件状态变量保存下来，称为"状态快照"。
                                实际上在React本身中会声明状态的"存在"----就像被束之高阁一样的存在于你的组件函数之外。
                                当React要调用你的组件时，React会为你提供该特定渲染的"状态快照"。 
                                而此时，你的组件函数会返回UI的快照，并在其JSX中包含一组新的"属性"和"事件处理程序"，而这些事依据React提供的"状态快照"计算的。

                                实际上，你组件的事件处理程序不会直接修改当前"状态快照"里的状态变量。相反，它会调用一个特殊的React函数来请求更新状态快照。在下次渲染时，React会再次调用你的组件函数，并提供更新后的状态快照。


                            */


                                /*
                                    这是一个小实验，向你展示这是如何工作的。在此示例中，你可能希望单击 “+3” 按钮会使计数器递增 3 次，因为它调用了 3 次 setNumber(number + 1)。
                                    看看当你点击 “+3” 按钮时会发生什么：
                                */ 

                                    import {useState} from 'react';
                                    
                                    export default function Counter(){
                                        const [number,setNumber] = useState(0);

                                        return (
                                            <div>
                                                <h1>{number}</h1>
                                                <button onClick={()=>{
                                                    setNumber(number+1);
                                                    setNumber(number+1);
                                                    setNumber(number+1);
                                                }}>
                                                    +3
                                                </button>
                                            </div>
                                        );
                                    }
                                    // 结果是，每次点击 "+3"按钮，只会增加一次！
                                /*
                                    设置状态只会更改下一次渲染的"状态快照"。不能更改当前的状态快照。
                                    所以，上面的例子中，每次调用时，当前状态快照中的number的值都是0.


                                     以下是此按钮的点击处理程序告诉 React 执行的操作：

                                                setNumber(number + 1)：number 是 0 所以 setNumber(0 + 1)。

                                                React 准备在下一次渲染时将 number 更改为 1。

                                                setNumber(number + 1)：number 是 0 所以 setNumber(0 + 1)。

                                                React 准备在下一次渲染时将 number 更改为 1。

                                                setNumber(number + 1)：number 是 0 所以 setNumber(0 + 1)。

                                                React 准备在下一次渲染时将 number 更改为 1。

                                         所以，你组件的事件处理程序对状态变量的更改，只是告诉React我在下一次渲染的状态快照中的某个状态变量要改成什么值。
                                */

                // 四、随时间变化的状态
                        // 现在猜猜，下面的按钮点击后会提示什么？
                                import {useState} from 'react';
                                export default function Counter(){
                                    <>
                                        <h1>{number}</h1>
                                        <button onClick={()=>{
                                            setNumber(number+5);
                                            alert(number);
                                        }}>
                                            +5
                                        </button>
                                    </>
                                }

                            // 结果是，它会提示0。
                            // 但如果你是用计时器，让alert在渲染完成之后执行呢？
                                export default function Counter() {
                                    const [number, setNumber] = useState(0);

                                    return (
                                        <>
                                            <h1>{number}</h1>
                                            <button onClick={() => {
                                                setNumber(number + 5);
                                                setTimeout(() => {
                                                alert(number);
                                                }, 3000);
                                            }}>+5</button>
                                        </>
                                    )
                                }

                            /*
                                哈哈，结果还是提示0 ！！

                                alert执行时，渲染已经完成，此时React中当前的"状态快照"中number的值已经是5了，但是个体 alert() 函数提供的number状态变量还是上一次渲染快照中的！

                                状态变量的值在渲染中永远不会发生改变，即使其事件处理程序的代码是异步的，是时间延迟的，都不会改变。
                                也就是说，事件处理程序中使用的状态变量的值"永远只能是当前渲染的状态快照中的值"   不会随时间改变而改变。

                                React会将状态变量的值"固定"保存在一个渲染器的事件处理程序中。你无需担心代码运行时状态值会发生任何改变。
                            */ 

                // 五、队列---一系列的状态更新

                            /*
                                这例子中有个问题，我们点击"+3"只会增加一次分数。
                            */

                                import { useState } from 'react';

                                export default function Counter() {
                                    const [score, setScore] = useState(0);

                                    function increment() {
                                        setScore(score + 1);
                                    }

                                    return (
                                        <>
                                            <button onClick={() => increment()}>+1</button>
                                            <button onClick={() => {
                                                increment();
                                                increment();
                                                increment();
                                            }}>+3</button>
                                            <h1>Score: {score}</h1>
                                        </>
                                    )
                                }

                            /*
                                在"状态快照"一节解释了为什么，因为状态变量的值在单次渲染中不会改变。
                                因此，你setScore(score+1)后，当前快照中score的值没有改变，依然是 0;
                            */
                                // 为了解决这个问题，你可以通过在设置状态时传递一个"更新程序函数"来解决此问题。
                                import { useState } from 'react';

                                export default function Counter() {
                                    const [score, setScore] = useState(0);

                                    function increment() {
                                        setScore(s => s + 1);
                                    }

                                    return (
                                        <>
                                            <button onClick={() => increment()}>+1</button>
                                            <button onClick={() => {
                                                increment();  // 这里是调用更新程序函数，而不是直接设置状态变量。
                                                increment();
                                                increment();
                                            }}>+3</button>
                                            <h1>Score: {score}</h1>
                                        </>
                                    )
                                }

        </script>
    </body>
</html>